package net.lab1024.sa.base.module.support.job.api;

import cn.hutool.core.util.IdUtil;
import com.google.common.collect.Lists;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.base.module.support.job.api.domain.SmartJobMsg;
import net.lab1024.sa.base.module.support.job.config.SmartJobAutoConfiguration;
import net.lab1024.sa.base.module.support.job.core.SmartJob;
import net.lab1024.sa.base.module.support.job.core.SmartJobExecutor;
import net.lab1024.sa.base.module.support.job.core.SmartJobLauncher;
import net.lab1024.sa.base.module.support.job.repository.SmartJobRepository;
import net.lab1024.sa.base.module.support.job.repository.domain.SmartJobEntity;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * smart job 执行端管理
 * 分布式系统之间 用发布/订阅消息的形式 来管理多个job
 *
 * @author huke
 * @date 2024/6/22 20:31
 */
@ConditionalOnBean(SmartJobAutoConfiguration.class)
@Slf4j
@Service
public class SmartJobClientManager {

    private final SmartJobLauncher jobLauncher;

    private final SmartJobRepository jobRepository;

    private final List<SmartJob> jobInterfaceList;

    private static final ConcurrentHashMap<String, ReentrantLock> MSG_LOCKS = new ConcurrentHashMap<>();

    public SmartJobClientManager(SmartJobLauncher jobLauncher,
                                 SmartJobRepository jobRepository,
                                 List<SmartJob> jobInterfaceList) {
        this.jobLauncher = jobLauncher;
        this.jobRepository = jobRepository;
        this.jobInterfaceList = jobInterfaceList;
        log.info("==== SmartJob ==== client-manager init");
    }

    /**
     * 处理消息 - 单体应用直接调用
     */
    public void handleMessage(SmartJobMsg msg) {
        log.info("==== SmartJob ==== handle-message :{}", msg);
        msg.setMsgId(IdUtil.fastSimpleUUID());
        // 判断消息类型
        SmartJobMsg.MsgTypeEnum msgType = msg.getMsgType();
        // 更新任务
        if (SmartJobMsg.MsgTypeEnum.UPDATE_JOB == msgType) {
            updateJob(msg.getJobId());
        }
        // 执行任务
        if (SmartJobMsg.MsgTypeEnum.EXECUTE_JOB == msgType) {
            executeJob(msg);
        }
    }

    /**
     * 获取任务执行类
     *
     * @param jobClass
     * @return
     */
    private Optional<SmartJob> queryJobImpl(String jobClass) {
        return jobInterfaceList.stream().filter(e -> Objects.equals(e.getClassName(), jobClass)).findFirst();
    }

    /**
     * 更新任务
     *
     * @param jobId
     */
    private void updateJob(Integer jobId) {
        SmartJobEntity jobEntity = jobRepository.getJobDao().selectById(jobId);
        if (null == jobEntity) {
            return;
        }
        jobLauncher.startOrRefreshJob(Lists.newArrayList(jobEntity));
    }

    /**
     * 立即执行任务
     *
     * @param msg
     */
    private void executeJob(SmartJobMsg msg) {
        Integer jobId = msg.getJobId();
        SmartJobEntity jobEntity = jobRepository.getJobDao().selectById(jobId);
        if (null == jobEntity) {
            return;
        }
        // 获取定时任务实现类
        Optional<SmartJob> optional = this.queryJobImpl(jobEntity.getJobClass());
        if (!optional.isPresent()) {
            return;
        }

        // 获取执行锁
        ReentrantLock rLock = MSG_LOCKS.computeIfAbsent(msg.getMsgId(), k -> new ReentrantLock());
        try {
            boolean getLock = rLock.tryLock();
            if (!getLock) {
                return;
            }
            
            // 通过执行器 执行任务
            jobEntity.setParam(msg.getParam());
            SmartJobExecutor jobExecutor = new SmartJobExecutor(jobEntity, jobRepository, optional.get());
            jobExecutor.execute(msg.getUpdateName());
        } finally {
            if (rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
            // 清理锁
            MSG_LOCKS.remove(msg.getMsgId());
        }
    }

    @PreDestroy
    public void destroy() {
        // 清理资源
        MSG_LOCKS.clear();
    }

}
